package com.guyu.common.web.handler;


import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.guyu.common.core.entity.BizResult;
import com.guyu.common.core.exception.BizException;
import com.guyu.common.core.exception.code.ErrorCodeEnum;
import com.guyu.common.core.util.GuyuStreamUtils;
import com.guyu.common.web.util.GuyuServletUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

/**
 * <p> 全局异常捕获（含业务异常捕获处理与运行时异常捕获打印到异常堆栈） </p>
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常捕捉处理
     */
    @ExceptionHandler(value = Exception.class)
    public BizResult defaultErrorHandler(HttpServletRequest request, Exception e) {
        log.error("[GUYU]未知异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        return BizResult.error(ErrorCodeEnum.UNKNOWN_ERROR);
    }

    /**
     * 业务异常捕捉处理
     */
    @ExceptionHandler(value = BizException.class)
    public BizResult bizExceptionHandler(HttpServletRequest request, BizException e) {
        log.error("[GUYU]自定义业务异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        return BizResult.error(e);
    }

    /**
     * 参数不合法异常捕捉处理
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    public BizResult Handler(HttpServletRequest request, IllegalArgumentException e) {
        log.error("[GUYU]参数不合法, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, e.getMessage());
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public BizResult httpRequestMethodNotSupportedExceptionHandler(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        log.error("[GUYU]不支持'{}'请求, url:[ {} ], param:[ {} ], body:[ {} ]",
                e.getMethod(), request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        return BizResult.error(ErrorCodeEnum.METHOD_NOT_ALLOWED, e.getMessage());
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public BizResult missingPathVariableExceptionHandler(HttpServletRequest request, MissingPathVariableException e) {
        log.error("[GUYU]请求路径中缺少必需的路径变量, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        String msg = String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName());
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, msg);
    }

    /**
     * 遗漏Servlet请求参数异常
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public BizResult missingServletRequestParameterExceptionHandler(HttpServletRequest request, MissingServletRequestParameterException e) {
        log.error("[GUYU]Servlet请求参数遗漏异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        String msg = String.format("请求缺少必需的参数[%s]", e.getParameterName());
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, msg);
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public BizResult methodArgumentTypeMismatchExceptionHandler(HttpServletRequest request, MethodArgumentTypeMismatchException e) {
        log.error("[GUYU]请求参数类型不匹配, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        String msg = String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.getName(), e.getRequiredType().getName(), e.getValue());
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, msg);
    }


    /**
     * 自定义验证异常
     */
    @ExceptionHandler(value = BindException.class)
    public BizResult bindExceptionHandler(HttpServletRequest request, BindException e) {
        log.error("[GUYU]参数绑定异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        String message = GuyuStreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public BizResult methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException e) {
        log.error("[GUYU]参数校验异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, message);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public BizResult constraintViolationExceptionHandler(HttpServletRequest request, ConstraintViolationException e) {
        log.error("[GUYU]约束违反异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        String msg = GuyuStreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, msg);
    }


    /**
     * Servlet处理请求时发生了错误或异常
     */
    @ExceptionHandler(value = ServletException.class)
    public BizResult servletExceptionHandler(HttpServletRequest request, ServletException e) {
        log.error("[GUYU]Servlet处理异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        return BizResult.error(ErrorCodeEnum.BAD_REQUEST);
    }

    /**
     * HTTP 请求消息体转换异常
     */
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public BizResult httpMessageNotReadableExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException e) {
        log.error("[GUYU]Http消息不可读异常, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT);
    }

    /**
     * HTTP 无效格式异常处理
     */
    @ExceptionHandler(value = InvalidFormatException.class)
    public BizResult invalidFormatExceptionHandler(HttpServletRequest request, InvalidFormatException e) {
        log.error("[GUYU]无效格式异常处理, url:[ {} ], param:[ {} ], body:[ {} ]",
                request.getRequestURI(), GuyuServletUtils.getParams(request), GuyuServletUtils.getRequestBody(request), e);
        // 无效格式异常处理。比如：目标格式为数值，输入为非数字的字符串（"80.5%"、"8.5.1"、"张三"）。
        return BizResult.error(ErrorCodeEnum.ILLEGAL_ARGUMENT, "参数数据类型不正确");
    }

}
